#include "Uart.h"
#include "Supervisor.h"
#include "hw/board.hpp"
#include <algorithm>
#include <cassert>

using namespace actor;

// 6s + "." + 6ms + " "
static constexpr int TIMESTAMP_SZ = 14;

constexpr auto get_self = []() { return sup.get_child<0>(); };

void Uart::initialize() {
  subscribe(&Uart::on_log_static);
  subscribe(&Uart::on_log_dynamic);
  subscribe(&Uart::on_commit);
  Parent::initialize();
  next_idx = free_idx = 0;
  in_progress = false;
}

void Uart::do_log(std::string_view msg) {
  auto sz = (int)msg.size();
  auto enough_space = (next_idx + TIMESTAMP_SZ + sz) < SZ;
  if (enough_space) {
    auto ts = hw::Board::get_now();
    auto ptr = buff + next_idx + TIMESTAMP_SZ;
    std::copy_n(msg.data(), sz, ptr);
    *(--ptr) = ' ';
    for (int i = 0; i < 6; ++i) {
      auto r = ts % 10;
      ts /= 10;
      *(--ptr) = r + '0';
    }
    *(--ptr) = '.';
    for (int i = 0; i < 6; ++i) {
      auto r = ts % 10;
      ts /= 10;
      *(--ptr) = r + '0';
    }

    next_idx += (sz + TIMESTAMP_SZ);
    if (!in_progress) {
      send_next();
    }
  }
}

void Uart::on_log_static(message::LogStatic &msg) { do_log(msg.string); }

void Uart::on_log_dynamic(message::LogDynamic &msg) {
  std::string_view message(msg.data, msg.size);
  do_log(message);
}

void Uart::send_next() {
  auto sz = next_idx - free_idx;
  assert(sz > 0);
  in_progress = true;
  hw::Board::uart_write(buff + free_idx, sz);
  free_idx = next_idx;
}

void Uart::on_commit(message::LogCommit &msg) {
  if (free_idx == next_idx) {
    next_idx = free_idx = 0;
    in_progress = false;
  } else {
    send_next();
  }
}

extern "C" {
void dma2_stream6_handler_backend() {
  auto self = get_self();
  auto ok = self->send<rl::ctx::interrupt, message::LogCommit>(0, self->get_id());
  assert(ok);
}
}
